package com.wiwf.model.magicapi.redis.module;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.redis.connection.DefaultStringRedisConnection;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPipelineException;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.util.ReflectionUtils;
import org.ssssssss.magicapi.core.annotation.MagicModule;
import org.ssssssss.script.functions.DynamicMethod;
import org.ssssssss.script.reflection.JavaReflection;

@MagicModule("redis")
public class RedisModule implements DynamicMethod {
    private final StringRedisTemplate redisTemplate;
    private final boolean isRedisson;

    public RedisModule(RedisConnectionFactory connectionFactory) {
        this.redisTemplate = new StringRedisTemplate(connectionFactory);
        this.isRedisson = Objects.equals("org.redisson.spring.data.connection.RedissonConnectionFactory", this.redisTemplate.getConnectionFactory().getClass().getName());
    }

    private byte[] serializer(Object value) {
        return value != null && !(value instanceof String) ? this.serializer(value.toString()) : this.redisTemplate.getStringSerializer().serialize((String)value);
    }

    private Object serializerForRedisson(Object value) {
        return value != null && !JavaReflection.isPrimitiveAssignableFrom(value.getClass(), value.getClass()) ? this.serializer(value.toString()) : value;
    }

    private Object deserialize(Object value) {
        if (value != null) {
            if (value instanceof byte[]) {
                return this.redisTemplate.getStringSerializer().deserialize((byte[])value);
            }

            if (value instanceof List) {
                List<Object> valueList = (List)value;
                List<Object> resultList = new ArrayList(valueList.size());
                Iterator var4 = valueList.iterator();

                while(var4.hasNext()) {
                    Object val = var4.next();
                    resultList.add(this.deserialize(val));
                }

                return resultList;
            }

            if (value instanceof Map) {
                Map<Object, Object> map = (Map)value;
                LinkedHashMap<Object, Object> newMap = new LinkedHashMap(map.size());
                map.forEach((key, valx) -> {
                    newMap.put(this.deserialize(key), this.deserialize(valx));
                });
                return newMap;
            }
        }

        return value;
    }

    public Object execute(String methodName, List<Object> parameters) {
        return this.redisTemplate.execute((connection) -> {
            Object result;
            if (this.isRedisson) {
                result = this.executeForRedisson(((DefaultStringRedisConnection)connection).getDelegate(), methodName, parameters);
            } else {
                byte[][] params = new byte[parameters.size()][];

                for(int i = 0; i < params.length; ++i) {
                    params[i] = this.serializer(parameters.get(i));
                }

                result = connection.execute(methodName, params);
            }

            return this.deserialize(result);
        }, this.isRedisson || this.redisTemplate.isExposeConnection());
    }

    private Object executeForRedisson(RedisConnection connection, String command, List<Object> parameters) {
        Method[] methods = connection.getClass().getDeclaredMethods();
        Method[] var5 = methods;
        int var6 = methods.length;

        for(int var7 = 0; var7 < var6; ++var7) {
            Method method = var5[var7];
            if (method.getName().equalsIgnoreCase(command) && Modifier.isPublic(method.getModifiers()) && method.getParameterTypes().length == parameters.size()) {
                try {
                    Object ret = this.execute(connection, method, parameters);
                    if (ret instanceof String) {
                        return ((String)ret).getBytes();
                    }

                    return ret;
                } catch (IllegalArgumentException var10) {
                    if (connection.isPipelined()) {
                        throw new RedisPipelineException(var10);
                    }

                    throw new InvalidDataAccessApiUsageException(var10.getMessage(), var10);
                }
            }
        }

        throw new UnsupportedOperationException();
    }

    private Object execute(RedisConnection connection, Method method, List<Object> parameters) {
        if (method.getParameterTypes().length > 0 && method.getParameterTypes()[0] == byte[][].class) {
            return ReflectionUtils.invokeMethod(method, connection, parameters.stream().map(this::serializer).toArray((x$0) -> {
                return new byte[x$0][];
            }));
        } else {
            return parameters.size() == 0 ? ReflectionUtils.invokeMethod(method, connection) : ReflectionUtils.invokeMethod(method, connection, parameters.stream().map(this::serializerForRedisson).toArray());
        }
    }
}
